use proc_macro2::TokenStream;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::{Data, DataStruct, Fields, FieldsNamed};

mod attrs;
mod length;

pub(crate) struct DeriveValidator {
    input: syn::DeriveInput,
}

type StructFields = Punctuated<syn::Field, syn::token::Comma>;

impl DeriveValidator {
    pub fn new(input: syn::DeriveInput) -> Self {
        Self { input }
    }

    /// 获取结构体的所有字段
    fn get_fields(&self) -> syn::Result<&StructFields> {
        if let Data::Struct(DataStruct {
            fields:
                Fields::Named(FieldsNamed {
                    named: ref struct_field,
                    ..
                }),
            ..
        }) = self.input.data
        {
            return Ok(struct_field);
        }
        Err(syn::Error::new_spanned(
            &self.input,
            "must define on a struct and struct must have named fields".to_owned(),
        ))
    }

    pub fn generate(&self) -> syn::Result<TokenStream> {
        let struct_name_ident = &self.input.ident;

        let struct_field = self.get_fields()?;
        let mut set_fields_token = TokenStream::new();

        for field in struct_field {
            let field_attr = attrs::get_fields_attr(field);
            if field_attr.is_none() {
                continue;
            }
            let field_attr = field_attr.unwrap();
            if let Some(v) = length::get_fields_length_token(&field_attr) {
                set_fields_token.extend(v);
            }
        }

        Ok(quote! {
            impl #struct_name_ident {
                fn validae(&self) -> Result<(), String> {
                    let errors = Vec::<String>::new();
                    #set_fields_token
                    if errors.is_empty() {
                        return Ok(())
                    }
                    Err(errors.join("\n"))
                }
            }
        })
    }
}
